import { isArray, isString } from ".";
// import { isReactive, toRaw } from "vue";
type obj = Record<string | number, any>;
// 深度克隆
export const deepClone = <T = Record<string, any>>(
  origin: T,
  target?: Record<string, any>
): T => {
  let tar = target || {};

  for (const key in origin) {
    if (Object.prototype.hasOwnProperty.call(origin, key)) {
      if (typeof origin[key] === "object" && origin[key] !== null) {
        tar[key] = isArray(origin[key]) ? [] : {};
        deepClone(origin[key], tar[key]);
      } else {
        tar[key] = origin[key];
      }
    }
  }

  return tar as T;
};

// JS对象深度合并
export const deepMerge = (target: obj = {}, source: obj = {}) => {
  target = deepClone(target);
  if (typeof target !== "object" || typeof source !== "object") return {};
  for (const prop in source) {
    if (!Object.prototype.hasOwnProperty.call(source, prop)) continue;
    if (prop in target) {
      if (typeof target[prop] !== "object") {
        target[prop] = source[prop];
      } else if (typeof source[prop] !== "object") {
        target[prop] = source[prop];
      } else if (target[prop].concat && source[prop].concat) {
        target[prop] = target[prop].concat(source[prop]);
      } else {
        target[prop] = deepMerge(target[prop], source[prop]);
      }
    } else {
      target[prop] = source[prop];
    }
  }
  return target;
};

// 获取某个对象下的属性，用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式，来自uview-ui的源码
// 解决了在vue的template中，调用一个可能不存在的对象深层次的属性会报错的问题，如直接在template中使用a.b.c.d的写法，当b不存在时，就会报错
export const getProperty = <T = any>(obj: any, key?: string): T | undefined => {
  if (!obj) {
    return undefined;
  }
  if (!isString(key)|| key === "") {
    return undefined;
  }
  if (key.indexOf(".") !== -1) {
    const keys = key.split(".");
    let firstObj = obj[keys[0]] || {};

    for (let i = 1; i < keys.length; i++) {
      if (firstObj) {
        firstObj = firstObj[keys[i]];
      }
    }
    return firstObj;
  }
  return obj[key];
};
interface SetProperty<T> {
  (obj: Record<string, any>, key: T, value: any): void;
}
// 设置对象的属性值，如果'a.b.c'的形式进行设置
export const setProperty: SetProperty<string> = (obj, key, value) => {
  if (!obj) {
    return;
  }
  // 递归赋值
  const inFn: SetProperty<Array<string>> = function (_obj, keys, v) {
    // 最后一个属性key
    if (keys.length === 1) {
      _obj[keys[0]] = v;
      return;
    }
    // 0~length-1个key
    while (keys.length > 1) {
      const k = keys[0];
      if (!_obj[k] || typeof _obj[k] !== "object") {
        _obj[k] = {};
      }
      const key = keys.shift();
      // 自调用判断是否存在属性，不存在则自动创建对象
      inFn(_obj[k], keys, v);
    }
  };

  if (typeof key !== "string" || key === "") {
  } else if (key.indexOf(".") !== -1) {
    // 支持多层级赋值操作
    const keys = key.split(".");
    inFn(obj, keys, value);
  } else {
    obj[key] = value;
  }
};
/**
 * 对对象或数组进行遍历，来自axios源码
 * Iterate over an Array or an Object invoking a function for each item.
 *
 * If `obj` is an Array callback will be called passing
 * the value, index, and complete array for each item.
 *
 * If 'obj' is an Object callback will be called passing
 * the value, key, and complete object for each property.
 *
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 */
export function forEach(
  obj: Record<any, any> | any[],
  fn: <T>(item: any, index: T extends any[] ? number : string, data: T) => void
) {
  // Don't bother if no value provided
  if (obj === null || typeof obj === "undefined") {
    return;
  }

  // Force an array if not already something iterable
  if (typeof obj !== "object") {
    /* eslint no-param-reassign:0 */
    obj = [obj];
  }

  if (isArray(obj)) {
    // Iterate over array values
    for (let i = 0, l = obj.length; i < l; i++) {
      fn(obj[i], i, obj);
    }
  } else {
    // Iterate over object keys
    for (const key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}
